use crate::model::auto_logic::RocketAutoLogic;

/// 火箭单位
/// base_mass:基本重量
#[derive(Debug, Clone)]
pub struct Rocket {
    info: RocketInfo,
    engines: Vec<Engine>,
    mounts: Vec<Mount>,
}

impl Rocket {
    pub fn new(info: RocketInfo, engines: Vec<Engine>, mounts: Vec<Mount>) -> Self {
        Rocket { info, engines, mounts }
    }

    /// 火箭运行 根据自动逻辑更改状态 然后执行
    pub fn _run(&mut self, now: u128, logic: impl RocketAutoLogic) -> f64 {
        logic.run(self, now);
        self.exec()
    }

    /// 火箭执行 消耗燃料并执行引擎 产生推力
    fn exec(&mut self) -> f64 {
        let mut f = 0.0;
        for e in &mut self.engines {
            f += e.run(&mut self.mounts);
        }
        f
    }
}

impl Rocket {
    /// 当前质量
    pub fn current_mass(&self) -> f64 {
        let mut mass = self.info.base_mass;
        for x in &self.mounts {
            mass += x.current_mass()
        }
        for x in &self.engines {
            mass += x.info.mass
        }
        mass
    }

    /// 当前燃料
    pub fn current_fuel(&self) -> u128 {
        let mut fuel = 0;
        for x in &self.mounts {
            fuel += x.fuel
        }
        fuel
    }

    /// 火箭信息
    pub fn info(&self) -> &RocketInfo {
        &self.info
    }

    /// 全部引擎启动
    pub fn ignition_all(&mut self) {
        for e in &mut self.engines {
            e.status = true;
        }
    }

    /// 当前推力 N
    pub fn current_thrust(&self) -> f64 {
        let mut thrust = 0.0;
        for e in &self.engines {
            if e.status {
                thrust += e.info.thrust;
            }
        }
        thrust
    }

    /// 当前推重比
    pub fn thrust_weight_ratio(&self) -> f64 {
        self.current_thrust() / self.current_mass()
    }

    /// 打印火箭当前信息
    pub fn show(&self) {
        let mut s = format!(" ~ ----\n  \
        current rocket status: mass: {}kg, fuel: {}kg,thrust_weight_ratio: {}\n  \
        mounts:", self.current_mass(), self.current_fuel(), self.thrust_weight_ratio());
        for i in 0..self.mounts.len() {
            s = format!("{} fuel[{}:{}kg]", s, i, self.mounts[i].fuel)
        }
        s = format!("{}\n  engines:\n", s);
        for i in 0..self.engines.len() {
            let e = &self.engines[i];
            s = format!("{}    engines[{}]: open: {}, running: {}s\n", s, e.info.name, e.status, e.running_time)
        }
        println!("{} ~ ----", s);
    }
}

/// 火箭信息\
/// base_mass 基本质量 kg\
/// air_drag_factor 空气阻力系数 0~1\
/// frontal_area 迎风面积 m^2
#[derive(Debug, Clone)]
pub struct RocketInfo {
    pub name: String,
    pub base_mass: f64,
    pub air_drag_factor: f64,
    pub frontal_area: f64,
}

impl RocketInfo {
    pub fn new(name: String, base_mass: f64, air_drag_factor: f64, frontal_area: f64) -> Self {
        RocketInfo { name, base_mass, air_drag_factor, frontal_area }
    }
}

/// 引擎
/// status 开关
/// info 信息
/// running_time 运行时长 s
#[derive(Debug, Clone)]
pub struct Engine {
    status: bool,
    info: EngineInfo,
    running_time: u128,
}

impl Engine {
    pub fn new(name: String, mass: f64, thrust: f64, fuel_consumption: u128) -> Self {
        let info = EngineInfo { name, mass, thrust, fuel_consumption };
        Engine { status: false, info, running_time: 0 }
    }

    /// 运行引擎 根据其开关 燃料 等因素 产出动力
    pub fn run(&mut self, mounts: &mut Vec<Mount>) -> f64 {
        if self.status {
            if self.filling(mounts) {
                return self.exec();
            } else {
                self.status = false;
            }
        }
        0.0
    }

    /// 装填燃料
    fn filling(&self, mounts: &mut Vec<Mount>) -> bool {
        for m in mounts {
            if m.spend_fuel(self.info.fuel_consumption) {
                return true;
            }
        }
        false
    }

    /// 执行引擎(已被输送燃料)
    /// 返回动力 N
    fn exec(&mut self) -> f64 {
        self.running_time += 1;
        self.info.thrust
    }
}

/// 引擎信息
/// name 名称
/// mass 质量 kg
/// thrust 推力 N
/// fuel_consumption 燃料消耗 kg/s
#[derive(Debug, Clone)]
pub struct EngineInfo {
    name: String,
    mass: f64,
    thrust: f64,
    fuel_consumption: u128,
}

impl EngineInfo {
    pub fn new(name: String, mass: f64, thrust: f64, fuel_consumption: u128) -> Self {
        EngineInfo { name, mass, thrust, fuel_consumption }
    }
}

/// 挂载
/// base_mass 基本质量 kg
/// fuel 燃料 m^3
/// fuel_density 燃料密度 kg/m^3
#[derive(Debug, Clone)]
pub struct Mount {
    base_mass: f64,
    fuel: u128,
    fuel_density: f64,
}

impl Mount {
    pub fn new(base_mass: f64, fuel: u128, fuel_density: f64) -> Self {
        Mount { base_mass, fuel, fuel_density }
    }

    /// 消耗燃料 返回是否成功
    /// 燃料不足的情况下会返回false
    pub fn spend_fuel(&mut self, kg: u128) -> bool {
        let enough = self.fuel > kg;
        if enough {
            self.fuel -= kg;
        }
        enough
    }

    /// 当前质量
    pub fn current_mass(&self) -> f64 {
        self.base_mass + self.fuel as f64 * self.fuel_density
    }
}
